// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <cantor/proto/parser/ast.pb.h>
#include <turbo/base/macros.h>

namespace cantor::unresolved {
    ////////////////////////////////////////////////////////////////////////////////
    /// @brief Trait to detect whether a type has `const pb::Location& location() const`
    ///
    /// Used in static_assert to enforce interface conformance in CRTP-based visitors.
    ////////////////////////////////////////////////////////////////////////////////
    template<typename T, typename = void>
    struct HasLocationMember : std::false_type {
    };

    template<typename T>
    struct HasLocationMember<T, std::enable_if_t<std::is_same_v<decltype(std::declval<const T&>().location_impl()),const pb::Location&>>>
            : std::true_type {
    };

#define CHECK_HAS_LOCATION(T) \
    static_assert(HasLocationMember<T>::value, TURBO_STRINGIFY(T) " must implement const pb::Location &location_impl() const");

    ////////////////////////////////////////////////////////////////////////////////
    /// @brief Trait to detect whether a type has `const pb::Status& error_status() const`
    ////////////////////////////////////////////////////////////////////////////////
    template<typename T, typename = void>
    struct HasErrorCodeMember : std::false_type {
    };

    template<typename T>
    struct HasErrorCodeMember<T, std::enable_if_t<std::is_same_v<decltype(std::declval<const std::decay_t<T>&>().
                    error_status_impl()),
                const pb::Status &> > >
            : std::true_type {
    };

#define CHECK_HAS_ERROR_CODE(T) \
    static_assert(HasErrorCodeMember<T>::value, TURBO_STRINGIFY(T) " must implement const pb::Status& error_status_impl() const");

#define CHECK_INTERFACE(T)         \
    CHECK_HAS_LOCATION(T);         \
    CHECK_HAS_ERROR_CODE(T)

    ////////////////////////////////////////////////////////////////////////////////
    /// @brief Base class for implementing AST visitors using CRTP.
    ///
    /// This class provides default traversal behavior for each node type.
    /// To implement a custom visitor, define a subclass that inherits from
    /// this base class and implements the desired `Visit` methods for node types
    /// of interest.
    ///
    /// The CRTP pattern enables static dispatch, avoiding the cost of dynamic
    /// dispatch while still allowing extensible behavior per node type.
    ///
    /// Example:
    /// ```cpp
    /// class MyVisitor : public AstVisitor<MyVisitor> {
    /// public:
    ///     void Visit(const SelectStmt &stmt) {
    ///         // Custom logic here...
    ///     }
    /// };
    /// ```
    ///
    /// @tparam Derived The user-defined visitor subclass.
    ///
    ////////////////////////////////////////////////////////////////////////////////
    template<typename Sub>
    class UnresolvedVisitor {
    public:
        virtual ~UnresolvedVisitor() = default;

        UnresolvedVisitor() {
        }

        int32_t location() const { return static_cast<const Sub *>(this)->location_impl().location(); }
        int32_t length() const { return static_cast<const Sub *>(this)->location_impl().len(); }
        int32_t error_code() const { return static_cast<const Sub *>(this)->error_status_impl().code(); }
        const std::string &error_msg() const { return errmsg_helper(static_cast<const Sub *>(this)->error_status_impl()); }

    private:
        const std::string &errmsg_helper(const pb::Status &status) {
            static const std::string errmsg = "";
            return status.has_msg() ? status.msg() : errmsg;
        }
    };
} // cantor::unresolved
